/*********************************************************************************
 *       Filename:  select_main.c
 *    Description:  
 *
 *        Version:  1.0.0(2024/12/09 13:23:21)
 *         Author:  Zhang Long <1318085470@qq.com>
 *      ChangeLog:  1, Release initial version on "2024/12/09 13:23:21"
 ********************************************************************************/

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <dirent.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <getopt.h>
#include <libgen.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <time.h>
#include <sqlite3.h>
#include <sys/epoll.h>
#include <poll.h>
#include <pthread.h>
#include <sys/select.h> 
#include "database.h"
#include "ds18b20.h"
#include "transferdata.h"
#include "localtime.h"
#include "socket.h"
#include "logger.h"
#include "proc.h"


#define MAX_EVENTS 512
#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))

static inline void print_usage(char *progname)
{
    printf("%s usage: \n", progname);
    printf("-i(--ipaddr): specify server IP address(also not specify, default is all)\n");
    printf("-p(--port):   specify server PORT\n");
	printf("-d(--debug):  specify program running in debug mode\n");
    printf("-h(--help):   print this help information\n");

	printf("\nExample: %s -p 12345 -d\n", progname);

    return ;
}
int main(int argc,char **argv)
{  
    int					log_level = LOG_LEVEL_INFO;

    int                 pack_bytes = 0;
    char			    *log_file = "server.log";
    char                *progname = NULL;
    int                 ch;
    char                *listen_ip = NULL;
	int			        listen_port = 0;   
    int                 daemon_run = 1; 
    long				log_size = 1000*1024;
    sqlite3 		    *db;
	char			    *db_file = "server.db";
    socket_t			sock = {0};
	int                 epoll_fd;
	struct epoll_event  event;
	struct epoll_event  event_array[MAX_EVENTS];
	int					events = 0;
	int 				i;
	int					connect_fd = -1;
    int                 max;
	struct sockaddr_in  client_addr;
	socklen_t			client_addr_len = sizeof(client_addr);
	int					rv = -1;
	char				pack_buf[1024] = {0};
	packet_t			pack = {0};
    int                 fds_array[1024];
    int                 found;
	char 				buf[1024];
	int					on =1;   
    fd_set              rdset;
    int                 maxfd=0;
    struct option opts[]=
    {
        {"ipaddr", required_argument, NULL, 'i'},
        {"port", required_argument, NULL, 'p'},
        {"debug", no_argument, NULL, 'd'},
        {"help", no_argument, NULL, 'h'},
        {NULL, 0, NULL, 0}
    };


    progname = basename(argv[0]);
    while ( -1 != (ch=getopt_long(argc, argv, "i:p:dh", opts, NULL)) )
	{
		switch (ch)
		{
			case 'i':
				listen_ip = optarg;
				break;

			case 'p':
				listen_port = atoi(optarg);
				break;

			case 'd':
				daemon_run = 0;
				log_file = "console";
				log_level = LOG_LEVEL_DEBUG;			
				break;

			case 'h':
				print_usage(progname);
				return 0;

			default:
				return -1;
		}
	}

    if( log_open(log_file, log_level, log_size, THREAD_LOCK_NONE) < 0 )
    {
        fprintf(stderr, "Initial log system failed\n");
        return 1;
    }
  	log_debug("log system open ok\n");

    if (!listen_port)
	{
		print_usage(progname);
		return -3;
	}
	log_debug("Parser arguments ok: IP[%s], PORT[%d], LOG[%s]\n", listen_ip, listen_port, log_file);

    set_socket_rlimit();
    install_signal();

    db_open(&db, db_file);

	db_create(db, TA_CREATE);
    
	if (daemon_run)
	{
		if (daemon(0, 0) < 0)
		{
			log_error("daemon() error: %s\n", strerror(errno));
			return -4;
		}
		else
		{
			log_info("Program running in daemon mode\n");
		}
	}
   
    socket_init(&sock, listen_ip, listen_port);
    if (socket_server_init(&sock) < 0)
	{
		log_error("ERROR: %s server listen on port %d failure\n", argv[0],listen_port);
		return -5;
	}
	log_info("%s server start to listen on port %d\n", argv[0],listen_port);
    for(i=0; i<ARRAY_SIZE(fds_array) ; i++)
    {
         fds_array[i]=-1;
    }
    fds_array[0] = sock.fd;
    while(!g_sigstop)
    {
        FD_ZERO(&rdset);
        for(i=0; i<ARRAY_SIZE(fds_array) ; i++)
        {
            if( fds_array[i] < 0 )
                    continue;
            maxfd = fds_array[i]>maxfd ? fds_array[i] : maxfd;
            FD_SET(fds_array[i], &rdset);
        }
        rv = select(maxfd+1, &rdset, NULL, NULL, NULL);
        if(rv < 0)
        {
            log_info("select failure: %s\n", strerror(errno));
            break;
        }
        else if(rv == 0)
        {
            log_info("select get timeout\n");
            continue;
        }
        if ( FD_ISSET(sock.fd, &rdset) )
        {
            if( (connect_fd=accept(sock.fd, (struct sockaddr *)NULL, NULL)) < 0)
            {
                log_error("accept new client failure: %s\n", strerror(errno));
                continue;
            }
            found = 0;
            for(i=0; i<ARRAY_SIZE(fds_array) ; i++)
            {
                if( fds_array[i] < 0 )
                {
                    printf("accept new client[%d] and add it into array\n", connect_fd );
                    fds_array[i] = connect_fd;
                    found = 1;
                    break;
                }
            }
            if( !found )
            {
                log_info("accept new client[%d] but full, so refuse it\n", connect_fd);
                close(connect_fd);
            }
        }
        else
        {
            for(i=0; i<ARRAY_SIZE(fds_array); i++)
            {
                if( fds_array[i]<0 || !FD_ISSET(fds_array[i], &rdset) )
                    continue;
                if( (rv=socket_read(fds_array[i], buf, sizeof(buf))) <= 0)
                {
                    log_info("socket[%d] read failure or get disconncet.\n", fds_array[i]);
                    close(fds_array[i]);
                    fds_array[i]=-1;
                }
                else
                {
                    log_info("Read %d bytes data[%s] from socket[%d] client[%s:%d] ok\n", rv, buf, fds_array[i], inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
                    packet_segmented_unpack(&pack, buf);		
					write_to_db(db, &pack);		
                }
            }

   
        }   
     }
  	socket_close(&sock);
	db_close(db, log_file);
	//close(sock.fd);
	log_close();
    return 0;
}